package modeling.editors;


import java.io.File;
import java.io.FileWriter;
import java.io.StringWriter;
import java.text.Collator;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import model.ProjectTableContentProvider;
import model.ProjectTableLabelProvider;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FontDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.*;
import org.eclipse.ui.dialogs.SaveAsDialog;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.MultiPageEditorPart;
import org.eclipse.ui.ide.IDE;

import swing2swt.layout.BorderLayout;
import util.FileUtil;
import util.XMLUtil;
import app.MConstants;

/**
 * An example showing how to create a multi-page editor.
 * This example has 3 pages:
 * <ul>
 * <li>page 0 contains a nested text editor.
 * <li>page 1 allows you to change the font used in page 2
 * <li>page 2 shows the words in page 0 in sorted order
 * </ul>
 */
public class ProjectQueryEditor extends MultiPageEditorPart implements IResourceChangeListener{
	private Table table;
	CheckboxTableViewer checkboxTableViewer;
	Map<String, String> queryMap;
	Text fromDateText;
	Text toDateText;
	Text count;
	String editorFilePath;
	SimpleDateFormat dateFormat = 
        new SimpleDateFormat("yyyy.MM");
	/** The text editor used in page 0. */
	private TextEditor editor;

	/** The font chosen in page 1. */
	private Font font;

	/** The text widget used in page 2. */
	private StyledText text;
	/**
	 * Creates a multi-page editor example.
	 */
	public ProjectQueryEditor() {
		super();
		ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
	}
	/**
	 * Creates page 0 of the multi-page editor,
	 * which contains a text editor.
	 */
	void createPage0() {

		Composite mainComposite = new Composite(getContainer(), SWT.NONE);
		mainComposite.setLayout(new BorderLayout(0, 0));

		final Composite composite = new Composite(mainComposite, SWT.NONE);
		composite.setLayoutData(BorderLayout.NORTH);
		
		//invisible text
		final Text text = new Text(composite, SWT.BORDER);
		
		final Button loadButton = new Button(composite, SWT.NONE);
		loadButton.setText("Load");
		loadButton.setBounds(0, 0, 44, 23);
		
		final Button saveAsButton = new Button(composite, SWT.NONE);
		saveAsButton.setText("Save As");
		saveAsButton.setBounds(50, 0, 51, 23);
		final Button queryButton = new Button(composite, SWT.NONE);
		queryButton.setText("Query");
		queryButton.setBounds(205, 0, 44, 23);

		final Label dateLabel = new Label(composite, SWT.NONE);
		dateLabel.setText("Date:");
		dateLabel.setBounds(255, 5, 25, 13);

		fromDateText = new Text(composite, SWT.BORDER);
		fromDateText.setBounds(283, 0, 59, 21);

		final Label toLabel = new Label(composite, SWT.NONE);
		toLabel.setText("To");
		toLabel.setBounds(343, 5, 12, 13);

		toDateText = new Text(composite, SWT.BORDER);
		toDateText.setBounds(358, 0, 59, 21);

		final Button checkButton = new Button(composite, SWT.CHECK);
		checkButton.setText("Check All");
		checkButton.setBounds(107, 3, 59, 16);

		count = new Text(composite, SWT.BORDER);
		count.setToolTipText("Count of Query Result");
		count.setBounds(175, 0, 25, 21);
		
		saveAsButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(final SelectionEvent e) {
//				SaveAsDialog   dialog   =   new   SaveAsDialog(composite.getShell());
////				IFile outputFile = project.getFile(<relative path>);
//				
//				
//				
//				dialog.setOriginalFile(((IFileEditorInput)getEditorInput()).getFile());   
//				dialog.open();   
				
				String saveAsPath = FileUtil.getFilePathFromDlg(getContainer().getShell(), MConstants.PROJECT_XML_FILTER_NAMES, MConstants.PROJECT_XML_FILTER_EXTENSIONS, text.getText().trim(), 1);
				if(saveAsPath!=null && !"".equals(saveAsPath)) { 
					Object[] checkedElements = checkboxTableViewer.getCheckedElements();
					Document document = DocumentHelper.createDocument(); 
					Element rootElement = document.addElement("projects");  
					for(int i=0; i<checkedElements.length; i++){
//						Element project=DocumentHelper.createElement("project");  
//						project.appendContent((Element)checkedElements[i]);
//						System.out.println("+++++++++++"+((Element)checkedElements[i]).getText());
						rootElement.add(((Element)checkedElements[i]).createCopy());
					}
						
					XMLUtil.doc2XmlFile(document,saveAsPath); 
					//refresh project
					IFile file = ((IFileEditorInput)getEditorInput()).getFile();
					IProject project = file.getProject(); 
					try {
						project.refreshLocal(IResource.DEPTH_INFINITE, null);
					} catch (CoreException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
				}
			}
		});
		
		queryButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(final SelectionEvent e) {
				if(!"".equals(text.getText().trim()))
				queryProjectTableEvent(text.getText().trim());
				count.setText(""+checkboxTableViewer.getTable().getItemCount());
			}
		});
		loadButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(final SelectionEvent e) {
				try {
					String projectModelPath = FileUtil.getFilePathFromDlg(getContainer().getShell(), MConstants.XML_FILTER_NAMES, MConstants.XML_FILTER_EXTENSIONS, text.getText().trim(), 0);
					if(projectModelPath!=null && !"".equals(projectModelPath)) {
						text.setText(projectModelPath);
						queryMap.put(MConstants.PROJECT_QUERY, projectModelPath);
						FileUtil.writeObject(queryMap, editorFilePath);
//						FileUtil.writeProperties(MConstants.PROJECT_MODEL_FILE_PATH, MConstants.PROJECT_MODEL, projectModelPath);
						handleProjectTableEvent(projectModelPath);
					}
				} catch (Exception e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		});
		checkButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(final SelectionEvent e) {
				if(checkButton.getSelection())
					checkboxTableViewer.setAllChecked(true);
				else
					checkboxTableViewer.setAllChecked(false);
			}
		});
		final Composite composite_1 = new Composite(mainComposite, SWT.NONE);
		composite_1.setLayout(new FillLayout());
		composite_1.setLayoutData(BorderLayout.CENTER);

		checkboxTableViewer = CheckboxTableViewer.newCheckList(composite_1, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION);
		checkboxTableViewer.setLabelProvider(new ProjectTableLabelProvider());
		checkboxTableViewer.setContentProvider(new ProjectTableContentProvider());
		table = checkboxTableViewer.getTable();
		table.setLinesVisible(true);
		table.setHeaderVisible(true);
		
		final TableColumn newColumnTableColumn = new TableColumn(table, SWT.NONE);
		newColumnTableColumn.setWidth(100);
		newColumnTableColumn.setText("ID");

		final TableColumn newColumnTableColumn_1 = new TableColumn(table, SWT.NONE);
		newColumnTableColumn_1.setWidth(100);
		newColumnTableColumn_1.setText("Project Name");

		final TableColumn newColumnTableColumn_2 = new TableColumn(table, SWT.NONE);
		newColumnTableColumn_2.setWidth(100);
		newColumnTableColumn_2.setText("Date");

		final TableColumn newColumnTableColumn_3 = new TableColumn(table, SWT.NONE);
		newColumnTableColumn_3.setWidth(100);
		newColumnTableColumn_3.setText("Duty");

		final TableColumn newColumnTableColumn_4 = new TableColumn(table, SWT.NONE);
		newColumnTableColumn_4.setWidth(100);
		newColumnTableColumn_4.setText("Subordination");

		final TableColumn newColumnTableColumn_5 = new TableColumn(table, SWT.NONE);
		newColumnTableColumn_5.setWidth(100);
		newColumnTableColumn_5.setText("Subject Code");

		final TableColumn newColumnTableColumn_6 = new TableColumn(table, SWT.NONE);
		newColumnTableColumn_6.setWidth(100);
		newColumnTableColumn_6.setText("Grant NO");

		final TableColumn newColumnTableColumn_7 = new TableColumn(table, SWT.NONE);
		newColumnTableColumn_7.setWidth(100);
		newColumnTableColumn_7.setText("Amount");
		
		String temp = queryMap.get(MConstants.PROJECT_QUERY);
		if(temp!=null) {
			handleProjectTableEvent(temp);
			text.setText(temp);
		}
		
		int index = addPage(mainComposite);
		setPageText(index, MConstants.PROJECT_QUERY);
	}
	
	protected void queryProjectTableEvent(String s) {
		//Zero, initial query conditions in a Map
		Map<String, String> conditionsMap = new HashMap<String, String>();
		initConsMap(conditionsMap);
		//First, organize the elements which satisfy the query condition
		Document doc = XMLUtil.read(s);
		boolean qMap = qMapIsBlank(conditionsMap);
		//Case 0: all input is ""
		if(qMap) {
			Iterator<Element> pIterator = doc.getRootElement().elementIterator("project");
			checkboxTableViewer.setInput(pIterator);
		}
		//Case 1
		if(!qMap) {
			List list = new ArrayList();
			Iterator<Element> pIterator = doc.getRootElement().elementIterator("project");
			for(;pIterator.hasNext();) {
				Element e = pIterator.next();
				if(elementSatisfyConditon(e, conditionsMap)) {
					list.add(e);
				}
			}
			checkboxTableViewer.setInput(list.iterator());
		}
		
		//Second, build table
		
	}
	/**
	 * 
	 * @param conditionsMap
	 * @return true when all values is ""
	 */
	private boolean qMapIsBlank(Map<String, String> conditionsMap) {
		Collection c = conditionsMap.values();
		Iterator i = c.iterator();
		for(;i.hasNext();) {
			if(!"".equals((String)i.next()))
				return false;
		}
		return true;
	}
	private boolean elementSatisfyConditon(Element e,
			Map<String, String> conditionsMap) {
		Element description = e.element("description");
		List<Element> nodes = description.elements();
		for(Element node : nodes) {
			if("date".equals(node.getName())) {
				String date = node.getText();
				try {
					return conDate(date,conditionsMap);
				} catch (ParseException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		}
		return false;
	}
	private boolean conDate(String date, Map<String, String> conditionsMap) throws ParseException {
		String from = conditionsMap.get("fromData").trim();
		String to = conditionsMap.get("toDate").trim();
		String mid = date.trim().split("-")[0];
		Date fDate = null;
		Date mDate = null;
		Date tDate = null;
		if("".equals(from)&&"".equals(to)) {
			return false;
		}
		else if("".equals(to)) {
			fDate = dateFormat.parse(from);
			mDate = dateFormat.parse(mid);
			if(fDate.before(mDate)||fDate.equals(mDate))
				return true;
			else 
				return false;
		}
		else if("".equals(from)) {
			mDate = dateFormat.parse(mid);
			tDate = dateFormat.parse(to);
			if(tDate.after(mDate)||tDate.equals(mDate))
				return true;
			else 
				return false;
		}
		else {
			fDate = dateFormat.parse(from);
			mDate = dateFormat.parse(mid);
			tDate = dateFormat.parse(to);
			if((fDate.before(mDate)||fDate.equals(mDate))&&(tDate.after(mDate)||tDate.equals(mDate)))
				return true;
			else 
				return false;
		}
	}
	private void initConsMap(Map<String, String> conditionsMap) {
		conditionsMap.put("fromData", fromDateText.getText().trim());
		conditionsMap.put("toDate", toDateText.getText().trim());
	}
	protected void handleProjectTableEvent(String s) {
		Document doc = XMLUtil.read(s);
		Iterator<Element> pIterator = doc.getRootElement().elementIterator("project");
		checkboxTableViewer.setInput(pIterator);
	}
	
	/**
	 * Creates page 1 of the multi-page editor,
	 * which allows you to change the font used in page 2.
	 */
	void createPage1() {

		Composite composite = new Composite(getContainer(), SWT.NONE);
		GridLayout layout = new GridLayout();
		composite.setLayout(layout);
		layout.numColumns = 2;

		Button fontButton = new Button(composite, SWT.NONE);
		GridData gd = new GridData(GridData.BEGINNING);
		gd.horizontalSpan = 2;
		fontButton.setLayoutData(gd);
		fontButton.setText("Change Font...");
		
		fontButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				setFont();
			}
		});

		int index = addPage(composite);
		setPageText(index, "Properties");
	}
	/**
	 * Creates page 2 of the multi-page editor,
	 * which shows the sorted text.
	 */
	void createPage2() {
		Composite composite = new Composite(getContainer(), SWT.NONE);
		FillLayout layout = new FillLayout();
		composite.setLayout(layout);
		text = new StyledText(composite, SWT.H_SCROLL | SWT.V_SCROLL);
		text.setEditable(false);

		int index = addPage(composite);
		setPageText(index, "Preview");
	}
	/**
	 * Creates the pages of the multi-page editor.
	 */
	protected void createPages() {
		createPage0();
//		createPage1();
//		createPage2();
	}
	/**
	 * The <code>MultiPageEditorPart</code> implementation of this 
	 * <code>IWorkbenchPart</code> method disposes all nested editors.
	 * Subclasses may extend.
	 */
	public void dispose() {
		ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
		super.dispose();
	}
	/**
	 * Saves the multi-page editor's document.
	 */
	public void doSave(IProgressMonitor monitor) {
		getEditor(0).doSave(monitor);
	}
	/**
	 * Saves the multi-page editor's document as another file.
	 * Also updates the text for page 0's tab, and updates this multi-page editor's input
	 * to correspond to the nested editor's.
	 */
	public void doSaveAs() {
		IEditorPart editor = getEditor(0);
		editor.doSaveAs();
		setPageText(0, editor.getTitle());
		setInput(editor.getEditorInput());
	}
	/* (non-Javadoc)
	 * Method declared on IEditorPart
	 */
	public void gotoMarker(IMarker marker) {
		setActivePage(0);
		IDE.gotoMarker(getEditor(0), marker);
	}
	/**
	 * The <code>MultiPageEditorExample</code> implementation of this method
	 * checks that the input is an instance of <code>IFileEditorInput</code>.
	 */
	public void init(IEditorSite site, IEditorInput editorInput)
		throws PartInitException {
		if (!(editorInput instanceof IFileEditorInput))
			throw new PartInitException("Invalid Input: Must be IFileEditorInput");
		super.init(site, editorInput);
		String editorFilePathPart1 = Platform.getInstanceLocation().getURL().getPath();
		String editorFilePathPart2 = ((IFileEditorInput)editorInput).getFile().getFullPath().toString();
		editorFilePath = editorFilePathPart1 + editorFilePathPart2;
		queryMap = (HashMap)FileUtil.readObject(editorFilePath);
		if(queryMap == null)
			queryMap = new HashMap<String, String>();
		this.setTitle(editorInput.getName());
	}
	/* (non-Javadoc)
	 * Method declared on IEditorPart.
	 */
	public boolean isSaveAsAllowed() {
		return true;
	}
	/**
	 * Calculates the contents of page 2 when the it is activated.
	 */
	protected void pageChange(int newPageIndex) {
		super.pageChange(newPageIndex);
		if (newPageIndex == 2) {
			sortWords();
		}
	}
	/**
	 * Closes all project files on project close.
	 */
	public void resourceChanged(final IResourceChangeEvent event){
		if(event.getType() == IResourceChangeEvent.PRE_CLOSE){
			Display.getDefault().asyncExec(new Runnable(){
				public void run(){
					IWorkbenchPage[] pages = getSite().getWorkbenchWindow().getPages();
					for (int i = 0; i<pages.length; i++){
						if(((FileEditorInput)editor.getEditorInput()).getFile().getProject().equals(event.getResource())){
							IEditorPart editorPart = pages[i].findEditor(editor.getEditorInput());
							pages[i].closeEditor(editorPart,true);
						}
					}
				}            
			});
		}
	}
	/**
	 * Sets the font related data to be applied to the text in page 2.
	 */
	void setFont() {
		FontDialog fontDialog = new FontDialog(getSite().getShell());
		fontDialog.setFontList(text.getFont().getFontData());
		FontData fontData = fontDialog.open();
		if (fontData != null) {
			if (font != null)
				font.dispose();
			font = new Font(text.getDisplay(), fontData);
			text.setFont(font);
		}
	}
	/**
	 * Sorts the words in page 0, and shows them in page 2.
	 */
	void sortWords() {

		String editorText =
			editor.getDocumentProvider().getDocument(editor.getEditorInput()).get();

		StringTokenizer tokenizer =
			new StringTokenizer(editorText, " \t\n\r\f!@#\u0024%^&*()-_=+`~[]{};:'\",.<>/?|\\");
		ArrayList editorWords = new ArrayList();
		while (tokenizer.hasMoreTokens()) {
			editorWords.add(tokenizer.nextToken());
		}

		Collections.sort(editorWords, Collator.getInstance());
		StringWriter displayText = new StringWriter();
		for (int i = 0; i < editorWords.size(); i++) {
			displayText.write(((String) editorWords.get(i)));
			displayText.write(System.getProperty("line.separator"));
		}
		text.setText(displayText.toString());
	}
}
